package com.odianyun.springcloud.internship.oms.startup.config;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.odianyun.db.mybatis.MapperFactoryBean;
import com.odianyun.db.mybatis.interceptor.BaseMapperInterceptor;
import com.odianyun.db.mybatis.interceptor.MybatisPageInterceptor;
import com.odianyun.project.support.base.configuration.EnableProjectMapper;
import com.odianyun.project.support.base.configuration.EnableProjectTx;
import com.odianyun.util.spring.SpringApplicationContext;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @description:
 * @author: EDZ
 * @time: 14:41
 * @date: 2021/7/26
 */
@EnableProjectTx(txAdviceBeanName = "txAdvice", txInterceptionPackage = "com.odianyun.springcloud.internship.oms.service", txAdvisorOrder = 2)
@EnableProjectMapper(datasourceBeanName = "dataSource", mapperAspectJExpression = "execution(* com.odianyun.springcloud.internship.oms.mapper..*.*(..))")
@MapperScan(basePackages = "com.odianyun.springcloud.internship.oms.mapper", sqlSessionFactoryRef = "sqlSessionFactory", factoryBean = MapperFactoryBean.class)
@Configuration
public class DaoConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public TransactionInterceptor txAdvice(PlatformTransactionManager transactionManager) {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        RuleBasedTransactionAttribute baseTx = new RuleBasedTransactionAttribute();
        baseTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Throwable.class)));
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(baseTx);
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        RuleBasedTransactionAttribute requiredNewTx = new RuleBasedTransactionAttribute(baseTx);
        requiredNewTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        RuleBasedTransactionAttribute requiredNestedTx = new RuleBasedTransactionAttribute(baseTx);
        requiredNestedTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
        source.setNameMap(ImmutableMap.of("*WithTx", requiredTx, "*WithNewTx", requiredNewTx, "*WithNestedTx", requiredNestedTx));
        return new TransactionInterceptor(transactionManager, source);
    }

    @DependsOn("mapperProvider")
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // datasource
        bean.setDataSource(dataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        /**
         * 自动装配改为手动，mybatis实体类包
         * 同mybatis.type-aliases-package=com.odianyun.springcloud.internship.oms.model
         */
        bean.setTypeAliasesPackage("com.odianyun.springcloud.internship.oms.model");
        List<Resource> resources = Lists.newArrayList();
        /**
         * mybatis mapper.xml
         * 同mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
         */
        resources.addAll(Arrays.asList(resolver.getResources("classpath:mybatis/mapper/*.xml")));
        bean.setMapperLocations(resources.toArray(new Resource[resources.size()]));

        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 同mybatis-config.xml中配置
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setLogImpl(Slf4jImpl.class);
        bean.setConfiguration(configuration);

        // BaseMapperInterceptor
        bean.setPlugins(new Interceptor[] {
                new MybatisPageInterceptor(),
                new BaseMapperInterceptor()
        });

        return bean;
    }

    @ConditionalOnMissingBean
    @Bean
    public SpringApplicationContext springAppCtx() {
        return new SpringApplicationContext();
    }
}
